﻿using DeHeng_Mes.Constant;
using DeHeng_Mes.Dao;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Model;
using DeHeng_Mes.Model.Boss;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Model.OrderSingleton;
using DeHeng_Mes.Model.PlcModel;
using DeHeng_Mes.Model.PlcModel.door_01;
using DeHeng_Mes.Model.Singleton;
using DeHeng_Mes.Model.Table;
using DeHeng_Mes.PlcService;
using DeHeng_Mes.Service;
using DeHeng_Mes.Utils;
using Microsoft.Extensions.Logging;
using NPOI.SS.Formula.Eval;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Tls;
using Org.BouncyCastle.Utilities;
using S7.Net;
using SqlSugar;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI;
using System.Xml.Linq;
using ZstdSharp.Unsafe;
using MappingTable = DeHeng_Mes.Utils.MappingTable;

namespace DeHeng_Mes.Thread
{
    public class LinePlcToMes
    {
        #region 成员实例
        OrderDao orderDao = new OrderDao();
        PlcDao plcDao = new PlcDao();
        DeviceDao deviceDao = new DeviceDao();
        List<PlcOrderConfigModel> readPlcList = PlcOrderConfigModelList.ReadPlcList;
        EntranceService entranceService = new EntranceService();
        ExitService exitService = new ExitService();
        VerificationService verificationService = new VerificationService();
        SpotCheakService spotCheakService = new SpotCheakService();
        ErrorType errortype = new ErrorType();
        #endregion

        /// <summary>
        /// 处理读取完成的信号，将追溯码与订单、节拍等信息绑定
        /// </summary>
        /// <param name="types">数据类型字典</param>
        /// <param name="orderModel">工单模型</param>
        public async void ProcessOkStatus(Dictionary<object, Type> types, PlcOrderConfigModel orderModel)
        {
            try
            {
                (byte[] bytes, List<string> stationList) = Tools.GetStatusBytes(orderModel);

                if (bytes == null && stationList == null)
                    return;

                int index = 0;

                foreach (string station in stationList)
                {
                    Stopwatch stopwatch = Stopwatch.StartNew();
                    // 获取旧状态值
                    int oldStatus = orderModel.stationStatus[station];

                    // 获取新状态值
                    int newStatus = S7.Net.Types.Int.FromByteArray(bytes.Skip(index * 50 + 2).Take(2).ToArray());

                    if (newStatus != 0 && newStatus != oldStatus)
                    {
                        string operation = MappingTable.GetOperationId(station);

                        // 重置状态位
                        orderModel.stationStatus[station] = newStatus;

                        // 获取当前操作类型
                        int typeIndex = S7.Net.Types.Int.FromByteArray(bytes.Skip(index * 50).Take(2).ToArray());

                        // 获取二维码
                        string code = GetCode(types, station);

                        code = code.Trim();

                        string typeString = string.Empty;

                        switch (typeIndex)
                        {
                            case 1:
                                typeString = "校验";
                                break;
                            case 2:
                                typeString = "入站";
                                break;
                            case 3:
                                typeString = "出站";
                                break;
                            case 4:
                                typeString = "点检";
                                break;
                            case 5:
                                typeString = "补序";
                                break;
                            case 6:
                                typeString = "结束时间增补";
                                break;
                            default:
                                typeString = "未知";
                                break;
                        }

                        Tools.ProduceLogText($"[{station}]触发[{typeString}]状态，关联码[{code}]，触发标识[{newStatus}]");

                        // 初始化结果
                        int isOk = 0;

                        // 初始化报警码
                        List<int> errorCodes = new List<int>();

                        try
                        {
                            // 1:校验 2:入站 3:出站
                            switch (typeIndex)
                            {
                                case 1:
                                    // 执行校验
                                    (isOk, errorCodes) = verificationService.CodeVerification(station, code, stationList);

                                    // 校验成功 执行备份
                                    //orderDao.BackUpCode(code, station);
                                    break;
                                case 2:
                                    if (string.IsNullOrEmpty(code))
                                    {
                                        Tools.ProduceLogTextError($"[{station}]触发[{typeString}]状态，关联码为[{code}]");

                                        isOk = 2;

                                        errorCodes.Add(1507);

                                        break;
                                    }

                                    // 执行入站
                                    (isOk, errorCodes) = entranceService.StationEntrance(station, code);
                                    break;
                                case 3:
                                    if (string.IsNullOrEmpty(code))
                                    {
                                        Tools.ProduceLogTextError($"[{station}]触发[{typeString}]状态，关联码为[{code}]");

                                        isOk = 2;

                                        errorCodes.Add(1508);

                                        break;
                                    }

                                    // 执行出站
                                    (isOk, errorCodes) = exitService.StationExit(station, index, bytes, code, operation);
                                    break;
                                case 4:
                                    // 执行点检
                                    (isOk, errorCodes) = spotCheakService.StationSpotChackSave(station, index, orderModel, plcDao.GetStringFieldValue(types, "Check", station));
                                    break;
                                case 5:
                                    // 执行补序
                                    (isOk, errorCodes) = errortype.InsertStation(station, code);
                                    break;
                                case 6:
                                    // 执行结束时间增补
                                    (isOk, errorCodes) = errortype.InsertEndStation(station, code);
                                    break;
                            }

                            /*// 首工位出入站做工单绑定
                            if(typeIndex == 2 || typeIndex == 3)
                            {
                                // 入站工单绑定
                                entranceService.CodeBandingOrder(station, code);
                            }*/
                        }
                        catch (Exception ex)
                        {
                            isOk = -1;

                            Tools.ProduceLogTextError($"[{typeString}]失败，原因是[{ex.Message}]");
                        }
                        finally
                        {
                            // 反馈类型
                            orderModel.plc.Write(DataType.DataBlock, 5001, index * 50, short.Parse(typeIndex.ToString()));

                            // 反馈结果值
                            orderModel.plc.Write(DataType.DataBlock, 5001, index * 50 + 2, short.Parse(isOk.ToString()));

                            // 反馈报警信息
                            ErrorToPlc(station, index, orderModel.plc, errorCodes);

                            await Task.Delay(10);

                            short v = (short)orderModel.plc.Read(DataType.DataBlock, 5001, index * 50, VarType.Int, 1); // 从DB5001读1个INT

                            short y = (short)orderModel.plc.Read(DataType.DataBlock, 5001, index * 50 + 2, VarType.Int, 1);

                            Tools.ProduceLogText($"[{station}]DB内反馈类型为[{v}]，反馈结果为[{y}]，关联码[{code}]");

                            if (v != typeIndex && v == 0)
                            {
                                //再次反馈类型
                                orderModel.plc.Write(DataType.DataBlock, 5001, index * 50, short.Parse(typeIndex.ToString()));
                                Tools.ProduceLogText($"[{station}]触发[{typeString}]状态不一致，DB内反馈类型为[{v}]，关联码[{code}]");
                            }

                            if (y != isOk && y == 0)
                            {
                                orderModel.plc.Write(DataType.DataBlock, 5001, index * 50 + 2, short.Parse(isOk.ToString()));
                                Tools.ProduceLogText($"[{station}]触发[{typeString}]状态不一致，DB内反馈结果为[{y}]，关联码[{code}]");
                            }

                            stopwatch.Stop();

                            long elapsedMilliseconds = stopwatch.ElapsedMilliseconds;

                            Tools.ProduceLogText($"[{station}]本次执行响应耗时[{elapsedMilliseconds}]MS");

                            if (typeIndex == 3 && !string.IsNullOrEmpty(code))
                            {
                                // 存储工艺数据
                                OperationDataSave(station, code, orderModel);
                            }

                            Tools.ProduceLogText($"[{station}]反馈[{typeString}]状态，反馈值[{isOk}]，反馈类型[{typeIndex}]，关联码[{code}]");
                        }
                    }

                    index++;
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"{orderModel.PlcName}处理读取完成的信号失败，原因是：" + ex.Message);
            }
        }

        /// <summary>
        /// 报警写入
        /// </summary>
        /// <param name="station"></param>
        /// <param name="index"></param>
        /// <param name="plc"></param>
        /// <param name="errorCodes"></param>
        private void ErrorToPlc(string station, int index, Plc plc, List<int> errorCodes)
        {
            if (errorCodes.Count <= 0)
                return;

            // 初始化索引缓存
            Dictionary<int, bool> plcIndex = new Dictionary<int, bool>();

            byte[] bytes1 = plc.ReadBytes(DataType.DataBlock, 5001, 0, 300);

            for (int i = 0; i <= 4; i++)
            {
                int nowIndex = i * 2 + (index * 50 + 4);

                int plcErrorCode = S7.Net.Types.Int.FromByteArray(bytes1.Skip(nowIndex).Take(2).ToArray());

                if (plcErrorCode == 0)
                {
                    plcIndex.Add(nowIndex, false);
                }
            }

            foreach (int errorCode in errorCodes)
            {
                int off = plcIndex.Where(x => !x.Value).Select(x => x.Key).FirstOrDefault();

                if (off <= 0)
                    break;

                // 执行报警
                plc.Write(DataType.DataBlock, 5001, off, short.Parse(errorCode.ToString()));

                plcIndex[off] = true;

                Tools.ProduceLogText($"{station}触发报警[{errorCode}]");
            }
        }

        /// <summary>
        /// 获取二维码
        /// </summary>
        /// <param name="types"></param>
        /// <param name="station"></param>
        /// <returns></returns>
        private string GetCode(Dictionary<object, Type> types, string station)
        {
            string code = plcDao.GetStringFieldValue(types, SystemConstant.Code, station);

            return code;
        }

        /// <summary>
        /// 根据工位判断数据存储类型
        /// </summary>
        /// <param name="station"></param>
        /// <param name="codesReturn"></param>
        /// <param name="bytes"></param>
        private void OperationDataSave(string station, string code, PlcOrderConfigModel orderModel)
        {
            string operationName = MappingTable.GetOperationNo(orderModel.PlcName, station);

            Tuple<int, int> tuple = MappingTable.GetBytesOff(orderModel.PlcName + "-" + operationName);

            if (tuple == null)
                return;

            byte[] bytes = orderModel.plc.ReadBytes(DataType.DataBlock, tuple.Item1, 0, tuple.Item2);

            switch (operationName)
            {
                case "WELD":
                    SaveWeldData(code, station, bytes, orderModel);
                    break;
                case "CCD":
                    SaveCCDData(code, station, bytes);
                    break;
                case "POINT":
                    SavePointData(code, station, bytes);
                    break;
            }
        }

        /// <summary>
        /// 存储打刻码数据
        /// </summary>
        /// <param name="codesReturn"></param>
        /// <param name="station"></param>
        /// <param name="bytes"></param>
        /// <param name="operation"></param>
        private void SavePointData(string code, string station, byte[] bytes)
        {
            string pointCode = S7.Net.Types.String.FromByteArray(bytes.Skip(130).Take(64).ToArray());

            pointCode = pointCode.Trim();

            int v = orderDao.InsertPointCode(code, pointCode, station);

            if (v > 0)
                Tools.ProduceLogText($"[{station}]存储了一条打码数据，追溯码[{code}]，明码[{pointCode}]，ID[{v}]");
        }

        /// <summary>
        /// 存储CCD数据
        /// </summary>
        /// <param name="codesReturn"></param>
        /// <param name="station"></param>
        /// <param name="bytes"></param>
        /// <param name="operation"></param>
        private void SaveCCDData(string code, string station, byte[] bytes)
        {
            // 初始化CCD集合
            List<TraceCCD> traceCCDs = new List<TraceCCD>();

            Dictionary<string, int> dic = MappingTable.GetCCDOff(station);

            if (dic == null)
                return;

            for (int i = 0; i < dic["Off"]; i++)
            {
                TraceCCD data = new TraceCCD();

                //data.Number = S7.Net.Types.String.FromByteArray(bytes.Skip(dic["Number"] + (i * 34)).Take(30).ToArray());

                byte[] bytes1 = bytes.Skip(dic["Number"] + (i * 34)).Take(30).ToArray();

                data.Number = Encoding.ASCII.GetString(bytes1, 2, bytes1[1]);

                data.Result = S7.Net.Types.Byte.FromByteArray(bytes.Skip(dic["Result"] + (i * 34)).Take(1).ToArray()).ToString();

                data.TraceId = code;

                traceCCDs.Add(data);
            }
             orderDao.SaveCCDData(traceCCDs , code);

            Tools.ProduceLogText($"[{station}]存储了一条CCD数据，追溯码[{code}]]");

            _ = Task.Run(() => { ViewService.InsertLineMesData(code, station, traceCCDs); });
        }

        /// <summary>
        /// 存储弧焊数据
        /// </summary>
        /// <param name="station"></param>
        /// <param name="bytes"></param>
        private void SaveWeldData(string code, string station, byte[] bytes, PlcOrderConfigModel orderModel)
        {
            Dictionary<string, int> dicOff = MappingTable.GetWeldOff(station);

            List<TraceWeld> weldDatas = new List<TraceWeld>();

            // 读取job数据
            byte[] jobBytes = orderModel.plc.ReadBytes(DataType.DataBlock, 5002, 0, 400);

            if (dicOff == null)
                return;

            if (dicOff != null)
            {
                for (int i = 0; i < dicOff["Off"]; i++)
                {
                    TraceWeld traceWeld = new TraceWeld();

                    traceWeld.TraceId = code;

                    traceWeld.SeamNo = S7.Net.Types.Int.FromByteArray(bytes.Skip(dicOff["SeamNo"] + 1618 * i).Take(2).ToArray()).ToString();
                    traceWeld.StartTime = S7.Net.Types.DateTime.FromByteArray(bytes.Skip(dicOff["StartTime"] + 1618 * i).Take(8).ToArray()).ToString();
                    traceWeld.EndTime = S7.Net.Types.DateTime.FromByteArray(bytes.Skip(dicOff["EndTime"] + 1618 * i).Take(8).ToArray()).ToString();
                    traceWeld.Station = station;

                    string[] weldI = new string[100];
                    string[] weldU = new string[100];
                    string[] wireSpeed = new string[100];
                    string[] gasFlow = new string[100];
                    string[] job = new string[10];

                    for (int j = 0; j < 100; j++)
                    {
                        weldI[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["WeldI"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();
                        weldU[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["WeldU"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();
                        wireSpeed[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["WireSpeed"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();
                        gasFlow[j] = S7.Net.Types.Real.FromByteArray(bytes.Skip(dicOff["GasFlow"] + 1618 * i + j * 4).Take(4).ToArray()).ToString();

                        if (j < 10)
                        {
                            job[j] = S7.Net.Types.Int.FromByteArray(jobBytes.Skip(dicOff["Job"] + 20 * i + j * 2).Take(2).ToArray()).ToString();
                        }
                    }

                    traceWeld.WeldI = string.Join(",", weldI);
                    traceWeld.WeldU = string.Join(",", weldU);
                    traceWeld.WireSpeed = string.Join(",", wireSpeed);
                    traceWeld.GasFlow = string.Join(",", gasFlow);
                    traceWeld.Job = string.Join(",", job);

                    weldDatas.Add(traceWeld);
                
                }
            }
            bool isSave = orderDao.WeldDataSave(weldDatas, code);

            Tools.ProduceLogText($"[{station}]存储了{weldDatas.Count}弧焊数据，追溯码[{code}]");

            _ = Task.Run(() => { ViewService.InsertLineMesData(code, station, weldDatas); });
        }
    }
}